package utils

import (
	"context"
	"fmt"
	"log"
	"strings"
	"sync"

	"github.com/mark3labs/mcp-go/mcp"
)

// CompatibilityMode defines the authentication compatibility mode
type CompatibilityMode int

const (
	// CompatibilityModeAuto automatically determines the best mode based on configuration
	CompatibilityModeAuto CompatibilityMode = iota
	// CompatibilityModeStaticOnly forces static authentication only
	CompatibilityModeStaticOnly
	// CompatibilityModeDynamicOnly forces dynamic authentication only
	CompatibilityModeDynamicOnly
	// CompatibilityModeHybrid allows both static and dynamic authentication
	CompatibilityModeHybrid
)

// AuthCompatibilityManager manages backward compatibility between static and dynamic authentication
type AuthCompatibilityManager struct {
	configManager    *AuthConfigManager
	mode             CompatibilityMode
	autoManaged      bool // tracks if mode is automatically managed
	mutex            sync.RWMutex
	
	// Legacy static authentication support
	staticAuthEnabled bool
	staticHeaders     map[string]string
	
	// Compatibility callbacks
	onModeChange []func(CompatibilityMode)
}

// NewAuthCompatibilityManager creates a new compatibility manager
func NewAuthCompatibilityManager(configManager *AuthConfigManager) *AuthCompatibilityManager {
	manager := &AuthCompatibilityManager{
		configManager:     configManager,
		mode:              CompatibilityModeAuto,
		autoManaged:       true,
		staticAuthEnabled: true,
		staticHeaders:     make(map[string]string),
		onModeChange:      make([]func(CompatibilityMode), 0),
	}
	
	// Register for configuration changes
	configManager.RegisterCallback(manager.OnConfigChange)
	
	return manager
}

// OnConfigChange handles configuration changes and adjusts compatibility mode
func (acm *AuthCompatibilityManager) OnConfigChange(config *DynamicAuthConfig) {
	acm.mutex.Lock()
	defer acm.mutex.Unlock()
	
	// Only auto-adjust if we're in auto-managed mode
	if !acm.autoManaged {
		return
	}
	
	// Determine the appropriate compatibility mode based on configuration
	if !config.Enabled {
		// If dynamic auth is disabled, use static only
		acm.setMode(CompatibilityModeStaticOnly)
	} else {
		// If dynamic auth is enabled, use hybrid mode
		acm.setMode(CompatibilityModeHybrid)
	}
}

// setMode sets the compatibility mode and notifies callbacks
func (acm *AuthCompatibilityManager) setMode(mode CompatibilityMode) {
	oldMode := acm.mode
	acm.mode = mode
	
	if oldMode != mode {
		log.Printf("[INFO] Authentication compatibility mode changed from %v to %v", oldMode, mode)
		
		// Notify callbacks
		for _, callback := range acm.onModeChange {
			func() {
				defer func() {
					if r := recover(); r != nil {
						log.Printf("[ERROR] Compatibility mode change callback panicked: %v", r)
					}
				}()
				callback(mode)
			}()
		}
	}
}

// SetMode explicitly sets the compatibility mode
func (acm *AuthCompatibilityManager) SetMode(mode CompatibilityMode) {
	acm.mutex.Lock()
	defer acm.mutex.Unlock()
	
	acm.autoManaged = (mode == CompatibilityModeAuto)
	acm.setMode(mode)
}

// GetMode returns the current compatibility mode
func (acm *AuthCompatibilityManager) GetMode() CompatibilityMode {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	return acm.mode
}

// RegisterModeChangeCallback registers a callback for mode changes
func (acm *AuthCompatibilityManager) RegisterModeChangeCallback(callback func(CompatibilityMode)) {
	acm.mutex.Lock()
	defer acm.mutex.Unlock()
	
	acm.onModeChange = append(acm.onModeChange, callback)
}

// ShouldUseDynamicAuth determines if dynamic authentication should be used
func (acm *AuthCompatibilityManager) ShouldUseDynamicAuth(authConfig *AuthConfig, session *sseSession) bool {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	switch acm.mode {
	case CompatibilityModeStaticOnly:
		return false
	case CompatibilityModeDynamicOnly:
		return true
	case CompatibilityModeHybrid, CompatibilityModeAuto:
		// Use dynamic auth if we have both auth config and session
		return authConfig != nil && session != nil && acm.configManager.IsEnabled()
	default:
		return false
	}
}

// ShouldUseStaticAuth determines if static authentication should be used
func (acm *AuthCompatibilityManager) ShouldUseStaticAuth(authConfig *AuthConfig, session *sseSession) bool {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	switch acm.mode {
	case CompatibilityModeStaticOnly:
		return acm.staticAuthEnabled
	case CompatibilityModeDynamicOnly:
		return false
	case CompatibilityModeHybrid, CompatibilityModeAuto:
		// Use static auth if dynamic auth is not available or not configured
		return acm.staticAuthEnabled && !acm.ShouldUseDynamicAuth(authConfig, session)
	default:
		return acm.staticAuthEnabled
	}
}

// CreateCompatibleToolHandler creates a tool handler that respects compatibility settings
func (acm *AuthCompatibilityManager) CreateCompatibleToolHandler(
	method string, 
	url string, 
	extraHeaders map[string]string, 
	session *sseSession, 
	authConfig *AuthConfig,
) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	
	// Determine which authentication method to use
	useDynamic := acm.ShouldUseDynamicAuth(authConfig, session)
	useStatic := acm.ShouldUseStaticAuth(authConfig, session)
	
	if useDynamic {
		// Use dynamic authentication with session and auth config
		return NewToolHandlerWithSession(method, url, extraHeaders, session, authConfig)
	} else if useStatic {
		// Use static authentication (legacy mode)
		return NewToolHandlerWithSession(method, url, extraHeaders, nil, nil)
	} else {
		// No authentication
		return NewToolHandlerWithSession(method, url, extraHeaders, nil, nil)
	}
}

// ValidateCompatibilityConfiguration validates that the compatibility configuration is consistent
func (acm *AuthCompatibilityManager) ValidateCompatibilityConfiguration() error {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	config := acm.configManager.GetConfig()
	
	// Check for conflicting configurations
	if acm.mode == CompatibilityModeDynamicOnly && !config.Enabled {
		return fmt.Errorf("dynamic-only mode requires dynamic authentication to be enabled")
	}
	
	if acm.mode == CompatibilityModeStaticOnly && !acm.staticAuthEnabled {
		return fmt.Errorf("static-only mode requires static authentication to be enabled")
	}
	
	return nil
}

// GetCompatibilityStatus returns a detailed status of the compatibility configuration
func (acm *AuthCompatibilityManager) GetCompatibilityStatus() map[string]interface{} {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	config := acm.configManager.GetConfig()
	
	return map[string]interface{}{
		"mode":                   acm.getModeString(acm.mode),
		"dynamic_auth_enabled":   config.Enabled,
		"static_auth_enabled":    acm.staticAuthEnabled,
		"can_use_dynamic":        config.Enabled && acm.mode != CompatibilityModeStaticOnly,
		"can_use_static":         acm.staticAuthEnabled && acm.mode != CompatibilityModeDynamicOnly,
		"configuration_valid":    acm.validateConfigurationInternal() == nil,
	}
}

// getModeString returns a string representation of the compatibility mode
func (acm *AuthCompatibilityManager) getModeString(mode CompatibilityMode) string {
	switch mode {
	case CompatibilityModeAuto:
		return "auto"
	case CompatibilityModeStaticOnly:
		return "static_only"
	case CompatibilityModeDynamicOnly:
		return "dynamic_only"
	case CompatibilityModeHybrid:
		return "hybrid"
	default:
		return "unknown"
	}
}

// validateConfigurationInternal validates configuration without locking (internal use)
func (acm *AuthCompatibilityManager) validateConfigurationInternal() error {
	config := acm.configManager.GetConfig()
	
	if acm.mode == CompatibilityModeDynamicOnly && !config.Enabled {
		return fmt.Errorf("dynamic-only mode requires dynamic authentication to be enabled")
	}
	
	if acm.mode == CompatibilityModeStaticOnly && !acm.staticAuthEnabled {
		return fmt.Errorf("static-only mode requires static authentication to be enabled")
	}
	
	return nil
}

// SetStaticAuthEnabled enables or disables static authentication support
func (acm *AuthCompatibilityManager) SetStaticAuthEnabled(enabled bool) {
	acm.mutex.Lock()
	defer acm.mutex.Unlock()
	
	acm.staticAuthEnabled = enabled
	
	// If static auth is disabled and we're in static-only mode, switch to auto mode
	if !enabled && acm.mode == CompatibilityModeStaticOnly {
		if acm.configManager.IsEnabled() {
			acm.setMode(CompatibilityModeDynamicOnly)
		} else {
			log.Printf("[WARNING] Static authentication disabled but dynamic authentication is also disabled")
		}
	}
}

// IsStaticAuthEnabled returns whether static authentication is enabled
func (acm *AuthCompatibilityManager) IsStaticAuthEnabled() bool {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	return acm.staticAuthEnabled
}

// AddStaticHeader adds a static authentication header
func (acm *AuthCompatibilityManager) AddStaticHeader(key, value string) {
	acm.mutex.Lock()
	defer acm.mutex.Unlock()
	
	acm.staticHeaders[key] = value
}

// RemoveStaticHeader removes a static authentication header
func (acm *AuthCompatibilityManager) RemoveStaticHeader(key string) {
	acm.mutex.Lock()
	defer acm.mutex.Unlock()
	
	delete(acm.staticHeaders, key)
}

// GetStaticHeaders returns a copy of the static authentication headers
func (acm *AuthCompatibilityManager) GetStaticHeaders() map[string]string {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	headers := make(map[string]string)
	for k, v := range acm.staticHeaders {
		headers[k] = v
	}
	return headers
}

// MergeHeaders merges static headers with provided headers, giving priority to provided headers
func (acm *AuthCompatibilityManager) MergeHeaders(providedHeaders map[string]string) map[string]string {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	merged := make(map[string]string)
	
	// Add static headers first
	for k, v := range acm.staticHeaders {
		merged[k] = v
	}
	
	// Override with provided headers
	for k, v := range providedHeaders {
		merged[k] = v
	}
	
	return merged
}

// HandleAuthenticationConflict handles conflicts between static and dynamic authentication
func (acm *AuthCompatibilityManager) HandleAuthenticationConflict(
	staticHeaders map[string]string,
	dynamicHeaders map[string]string,
) map[string]string {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	result := make(map[string]string)
	
	// Copy static headers first
	for k, v := range staticHeaders {
		result[k] = v
	}
	
	// Handle conflicts based on compatibility mode
	switch acm.mode {
	case CompatibilityModeStaticOnly:
		// Keep only static headers, ignore dynamic
		return result
		
	case CompatibilityModeDynamicOnly:
		// Use only dynamic headers
		return dynamicHeaders
		
	case CompatibilityModeHybrid, CompatibilityModeAuto:
		// Dynamic headers take precedence over static headers
		for k, v := range dynamicHeaders {
			if existingValue, exists := result[k]; exists {
				log.Printf("[INFO] Dynamic authentication header '%s' overriding static header (was: %s, now: %s)", 
					k, maskSensitiveValue(k, existingValue), maskSensitiveValue(k, v))
			}
			result[k] = v
		}
		return result
		
	default:
		// Default to dynamic precedence
		for k, v := range dynamicHeaders {
			result[k] = v
		}
		return result
	}
}

// maskSensitiveValue masks sensitive authentication values for logging
func maskSensitiveValue(key, value string) string {
	key = strings.ToLower(key)
	if strings.Contains(key, "auth") || strings.Contains(key, "token") || 
	   strings.Contains(key, "key") || strings.Contains(key, "secret") {
		if len(value) <= 8 {
			return "***"
		}
		return value[:4] + "***" + value[len(value)-4:]
	}
	return value
}